#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "../src/storage/backup_system.h"
#include "../src/utils/logger.h"
#include "../include/gudb.h"

// Mock database creation
gudb* create_mock_database() {
    gudb* db = calloc(1, sizeof(gudb));
    if (db) {
        strcpy(db->data_dir, "test_backup_db");
        db->data_fd = -1;
        db->wal_fd = -1;
        db->binlog_fd = -1;
        db->meta_fd = -1;
        db->log_fd = -1;
        db->lock_fd = -1;
    }
    return db;
}

// Test backup system creation and initialization
void test_backup_system_creation() {
    printf("Testing backup system creation and initialization...\n");
    
    // Setup logger
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_backup_system.log");
    logger_config.enable_console_output = true;
    
    logger_t* logger = logger_create(&logger_config);
    assert(logger != NULL);
    logger_init(logger);
    logger_set_global(logger);
    
    // Create mock database
    gudb* db = create_mock_database();
    assert(db != NULL);
    
    // Test default configuration
    backup_config_t config = backup_system_default_config();
    assert(strcmp(config.backup_directory, "./backups") == 0);
    assert(config.default_type == BACKUP_TYPE_FULL);
    assert(config.compression == COMPRESSION_GZIP);
    assert(config.encryption == ENCRYPTION_NONE);
    assert(config.max_backup_threads == 4);
    assert(config.retention_days == 30);
    assert(config.enable_checksum == true);
    
    // Create backup system
    backup_system_t* system = backup_system_create(&config, db);
    assert(system != NULL);
    assert(system->database == db);
    assert(system->next_backup_id == 1);
    assert(system->next_job_id == 1);
    assert(system->initialized == false);
    
    // Initialize system
    int result = backup_system_init(system, logger);
    assert(result == 0);
    assert(system->initialized == true);
    assert(system->logger == logger);
    
    // Test statistics
    backup_system_stats_t stats;
    result = backup_system_get_stats(system, &stats);
    assert(result == 0);
    assert(stats.total_backups == 0);
    assert(stats.successful_backups == 0);
    assert(stats.failed_backups == 0);
    
    // Print initial statistics
    backup_system_print_stats(system);
    
    // Cleanup
    backup_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Backup system creation test passed!\n\n");
}

// Test backup job creation and management
void test_backup_job_management() {
    printf("Testing backup job creation and management...\n");
    
    // Setup
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_backup_system.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    backup_config_t config = backup_system_default_config();
    strcpy(config.backup_directory, "./test_backups");
    
    backup_system_t* system = backup_system_create(&config, db);
    backup_system_init(system, logger);
    
    // Create backup jobs
    uint64_t job1 = backup_system_create_backup(system, BACKUP_TYPE_FULL, "Full backup test");
    assert(job1 > 0);
    printf("Created full backup job: %llu\n", job1);
    
    uint64_t job2 = backup_system_create_backup(system, BACKUP_TYPE_INCREMENTAL, "Incremental backup test");
    assert(job2 > 0);
    printf("Created incremental backup job: %llu\n", job2);
    
    uint64_t job3 = backup_system_create_backup(system, BACKUP_TYPE_DIFFERENTIAL, "Differential backup test");
    assert(job3 > 0);
    printf("Created differential backup job: %llu\n", job3);
    
    // Test job retrieval
    backup_job_t* job = backup_system_get_job(system, job1);
    assert(job != NULL);
    assert(job->job_id == job1);
    assert(job->backup_type == BACKUP_TYPE_FULL);
    assert(job->status == BACKUP_STATUS_RUNNING || job->status == BACKUP_STATUS_PENDING);
    
    // Wait for jobs to complete (they run in background threads)
    sleep(1);
    
    // Check job status
    job = backup_system_get_job(system, job1);
    printf("Job %llu status: %s, progress: %.1f%%\n", 
           job1, backup_status_to_string(job->status), job->progress_percent);
    
    job = backup_system_get_job(system, job2);
    printf("Job %llu status: %s, progress: %.1f%%\n", 
           job2, backup_status_to_string(job->status), job->progress_percent);
    
    job = backup_system_get_job(system, job3);
    printf("Job %llu status: %s, progress: %.1f%%\n", 
           job3, backup_status_to_string(job->status), job->progress_percent);
    
    // Test invalid job ID
    backup_job_t* invalid_job = backup_system_get_job(system, 99999);
    assert(invalid_job == NULL);
    
    // Cleanup
    backup_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Backup job management test passed!\n\n");
}

// Test backup manifest creation and management
void test_backup_manifest_management() {
    printf("Testing backup manifest creation and management...\n");
    
    // Setup
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_backup_system.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    backup_config_t config = backup_system_default_config();
    strcpy(config.backup_directory, "./test_backups");
    
    backup_system_t* system = backup_system_create(&config, db);
    backup_system_init(system, logger);
    
    // Create backup manifests
    backup_manifest_t* manifest1 = backup_system_create_manifest(
        system, BACKUP_TYPE_FULL, "Test full backup manifest");
    assert(manifest1 != NULL);
    assert(manifest1->backup_type == BACKUP_TYPE_FULL);
    assert(manifest1->is_valid == true);
    assert(manifest1->is_archived == false);
    printf("Created manifest with ID: %llu\n", manifest1->backup_id);
    
    backup_manifest_t* manifest2 = backup_system_create_manifest(
        system, BACKUP_TYPE_INCREMENTAL, "Test incremental backup manifest");
    assert(manifest2 != NULL);
    assert(manifest2->backup_type == BACKUP_TYPE_INCREMENTAL);
    printf("Created manifest with ID: %llu\n", manifest2->backup_id);
    
    // Save manifests
    int result = backup_system_save_manifest(system, manifest1);
    assert(result == 0);
    
    result = backup_system_save_manifest(system, manifest2);
    assert(result == 0);
    
    // List backups
    backup_manifest_t** manifests;
    uint32_t count;
    result = backup_system_list_backups(system, &manifests, &count);
    assert(result == 0);
    assert(count == 2);
    
    printf("Listed %u backup manifests:\n", count);
    for (uint32_t i = 0; i < count; i++) {
        printf("  Backup %llu: %s - %s\n", 
               manifests[i]->backup_id,
               backup_type_to_string(manifests[i]->backup_type),
               manifests[i]->description);
    }
    
    free(manifests);
    
    // Cleanup
    backup_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Backup manifest management test passed!\n\n");
}

// Test file operations
void test_file_operations() {
    printf("Testing backup file operations...\n");
    
    // Create a test file
    const char* test_file = "test_backup_file.txt";
    FILE* file = fopen(test_file, "w");
    assert(file != NULL);
    fprintf(file, "This is a test file for backup system testing.\n");
    fprintf(file, "It contains some sample data to verify checksum calculation.\n");
    fclose(file);
    
    // Test checksum calculation
    uint64_t checksum = calculate_file_checksum(test_file);
    assert(checksum != 0);
    printf("File checksum: %llu\n", checksum);
    
    // Calculate again to verify consistency
    uint64_t checksum2 = calculate_file_checksum(test_file);
    assert(checksum == checksum2);
    printf("Checksum verification passed\n");
    
    // Test compression (placeholder implementation)
    const char* compressed_file = "test_backup_file.txt.gz";
    int result = compress_file(test_file, compressed_file, COMPRESSION_GZIP);
    assert(result == 0);
    printf("File compression completed\n");
    
    // Test decompression (placeholder implementation)
    const char* decompressed_file = "test_backup_file_restored.txt";
    result = decompress_file(compressed_file, decompressed_file, COMPRESSION_GZIP);
    assert(result == 0);
    printf("File decompression completed\n");
    
    // Test invalid file checksum
    uint64_t invalid_checksum = calculate_file_checksum("nonexistent_file.txt");
    assert(invalid_checksum == 0);
    
    // Cleanup test files
    unlink(test_file);
    unlink(compressed_file);
    unlink(decompressed_file);
    
    printf("File operations test passed!\n\n");
}

// Test backup verification
void test_backup_verification() {
    printf("Testing backup verification...\n");
    
    // Setup
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_backup_system.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    backup_config_t config = backup_system_default_config();
    strcpy(config.backup_directory, "./test_backups");
    
    backup_system_t* system = backup_system_create(&config, db);
    backup_system_init(system, logger);
    
    // Create and save a manifest with file information
    backup_manifest_t* manifest = backup_system_create_manifest(
        system, BACKUP_TYPE_FULL, "Test verification manifest");
    
    // Create test files for verification
    const char* test_file1 = "test_verify_file1.txt";
    const char* test_file2 = "test_verify_file2.txt";
    
    FILE* file1 = fopen(test_file1, "w");
    fprintf(file1, "Test file 1 content\n");
    fclose(file1);
    
    FILE* file2 = fopen(test_file2, "w");
    fprintf(file2, "Test file 2 content\n");
    fclose(file2);
    
    // Add file information to manifest
    manifest->file_count = 2;
    manifest->files = calloc(2, sizeof(backup_file_info_t));
    
    strcpy(manifest->files[0].filename, "test_verify_file1.txt");
    strcpy(manifest->files[0].full_path, test_file1);
    manifest->files[0].checksum = calculate_file_checksum(test_file1);
    manifest->files[0].file_size = 100; // Approximate
    
    strcpy(manifest->files[1].filename, "test_verify_file2.txt");
    strcpy(manifest->files[1].full_path, test_file2);
    manifest->files[1].checksum = calculate_file_checksum(test_file2);
    manifest->files[1].file_size = 100; // Approximate
    
    backup_system_save_manifest(system, manifest);
    
    // Test backup verification
    int result = backup_system_verify_backup(system, manifest->backup_id);
    assert(result == 0); // Should pass verification
    printf("Backup verification passed for backup %llu\n", manifest->backup_id);
    
    // Test verification of non-existent backup
    result = backup_system_verify_backup(system, 99999);
    assert(result == -1); // Should fail
    
    // Modify a file to test checksum failure
    FILE* file1_mod = fopen(test_file1, "w");
    fprintf(file1_mod, "Modified content\n");
    fclose(file1_mod);
    
    result = backup_system_verify_backup(system, manifest->backup_id);
    printf("Backup verification with modified file result: %d\n", result);
    
    // Cleanup
    unlink(test_file1);
    unlink(test_file2);
    backup_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Backup verification test passed!\n\n");
}

// Test thread management
void test_thread_management() {
    printf("Testing backup system thread management...\n");
    
    // Setup
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_backup_system.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    backup_config_t config = backup_system_default_config();
    strcpy(config.backup_directory, "./test_backups");
    
    backup_system_t* system = backup_system_create(&config, db);
    backup_system_init(system, logger);
    
    // Test starting threads
    int result = backup_system_start_threads(system);
    assert(result == 0);
    assert(system->threads_running == true);
    printf("Background threads started successfully\n");
    
    // Let threads run for a short time
    sleep(2);
    
    // Test stopping threads
    result = backup_system_stop_threads(system);
    assert(result == 0);
    assert(system->threads_running == false);
    printf("Background threads stopped successfully\n");
    
    // Test starting threads again
    result = backup_system_start_threads(system);
    assert(result == 0);
    printf("Background threads restarted successfully\n");
    
    // Cleanup (destroy will stop threads automatically)
    backup_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Thread management test passed!\n\n");
}

// Test statistics and monitoring
void test_statistics_monitoring() {
    printf("Testing backup system statistics and monitoring...\n");
    
    // Setup
    logger_config_t logger_config = logger_default_config();
    strcpy(logger_config.log_file_path, "test_backup_system.log");
    logger_config.enable_console_output = false;
    
    logger_t* logger = logger_create(&logger_config);
    logger_init(logger);
    logger_set_global(logger);
    
    gudb* db = create_mock_database();
    backup_config_t config = backup_system_default_config();
    strcpy(config.backup_directory, "./test_backups");
    
    backup_system_t* system = backup_system_create(&config, db);
    backup_system_init(system, logger);
    backup_system_start_threads(system);
    
    // Get initial statistics
    backup_system_stats_t stats_before;
    int result = backup_system_get_stats(system, &stats_before);
    assert(result == 0);
    
    // Create some backup jobs
    backup_system_create_backup(system, BACKUP_TYPE_FULL, "Stats test backup 1");
    backup_system_create_backup(system, BACKUP_TYPE_INCREMENTAL, "Stats test backup 2");
    
    // Wait for jobs to process
    sleep(2);
    
    // Update and get statistics
    backup_system_update_stats(system);
    
    backup_system_stats_t stats_after;
    result = backup_system_get_stats(system, &stats_after);
    assert(result == 0);
    
    printf("Statistics comparison:\n");
    printf("  Active jobs before: %u, after: %u\n", 
           stats_before.active_jobs, stats_after.active_jobs);
    
    // Print comprehensive statistics
    backup_system_print_stats(system);
    
    // Cleanup
    backup_system_destroy(system);
    free(db);
    logger_destroy(logger);
    
    printf("Statistics and monitoring test passed!\n\n");
}

// Test utility functions
void test_utility_functions() {
    printf("Testing backup system utility functions...\n");
    
    // Test backup type string conversion
    assert(strcmp(backup_type_to_string(BACKUP_TYPE_FULL), "FULL") == 0);
    assert(strcmp(backup_type_to_string(BACKUP_TYPE_INCREMENTAL), "INCREMENTAL") == 0);
    assert(strcmp(backup_type_to_string(BACKUP_TYPE_DIFFERENTIAL), "DIFFERENTIAL") == 0);
    assert(strcmp(backup_type_to_string(BACKUP_TYPE_LOG), "LOG") == 0);
    assert(strcmp(backup_type_to_string(999), "UNKNOWN") == 0);
    
    // Test backup status string conversion
    assert(strcmp(backup_status_to_string(BACKUP_STATUS_PENDING), "PENDING") == 0);
    assert(strcmp(backup_status_to_string(BACKUP_STATUS_RUNNING), "RUNNING") == 0);
    assert(strcmp(backup_status_to_string(BACKUP_STATUS_COMPLETED), "COMPLETED") == 0);
    assert(strcmp(backup_status_to_string(BACKUP_STATUS_FAILED), "FAILED") == 0);
    assert(strcmp(backup_status_to_string(BACKUP_STATUS_CANCELLED), "CANCELLED") == 0);
    assert(strcmp(backup_status_to_string(999), "UNKNOWN") == 0);
    
    // Test recovery type string conversion
    assert(strcmp(recovery_type_to_string(RECOVERY_TYPE_FULL), "FULL") == 0);
    assert(strcmp(recovery_type_to_string(RECOVERY_TYPE_POINT_IN_TIME), "POINT_IN_TIME") == 0);
    assert(strcmp(recovery_type_to_string(RECOVERY_TYPE_TRANSACTION), "TRANSACTION") == 0);
    assert(strcmp(recovery_type_to_string(RECOVERY_TYPE_PAGE), "PAGE") == 0);
    assert(strcmp(recovery_type_to_string(999), "UNKNOWN") == 0);
    
    printf("All utility function tests passed\n");
    printf("Utility functions test passed!\n\n");
}

int main() {
    printf("=== GuDB Backup System Test ===\n\n");
    
    // Ensure test backup directory exists
    mkdir("./test_backups", 0755);
    
    test_backup_system_creation();
    test_backup_job_management();
    test_backup_manifest_management();
    test_file_operations();
    test_backup_verification();
    test_thread_management();
    test_statistics_monitoring();
    test_utility_functions();
    
    printf("=== All backup system tests completed successfully! ===\n");
    printf("Check the generated log file: test_backup_system.log\n");
    printf("Backup files created in: ./test_backups/\n");
    
    return 0;
}